package br.edu.ifce.mflj.conectividade;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import br.edu.ifce.mflj.comunicacao.Pacote;
import br.edu.ifce.mflj.comunicacao.TipoPacote;
import br.edu.ifce.mflj.dados.Usuario;
import br.edu.ifce.mflj.exception.PacoteInvalidoException;
import br.edu.ifce.mflj.observer.ChatListener;
import br.edu.ifce.mflj.observer.UsuarioListener;

public class ClienteSocket extends SocketGenerico {

	private Queue<Pacote>				bufferDeSaida;

	private List<ChatListener>			chatListeners;
	private List<MouseListener>			mouseListeners;
	private List<MouseMotionListener>	mouseMotionListeners;
	private List<UsuarioListener>		usuarioListeners;

	public ClienteSocket( String endereco, Integer porta ) throws PacoteInvalidoException {
		super( endereco, porta );

		bufferDeSaida			= new LinkedList<Pacote>();

		chatListeners			= new ArrayList<ChatListener>();
		mouseListeners			= new ArrayList<MouseListener>();
		mouseMotionListeners	= new ArrayList<MouseMotionListener>();
		usuarioListeners		= new ArrayList<UsuarioListener>();
	}

	public void enviarPacote( Pacote pacote ){
		this.bufferDeSaida.add( pacote );
	}

	public void iniciarComunicacao( Usuario usuario ){
		ObjectInputStream streamEntrada = null;

		new Thread( this ).start();

		try {
			enviarPacote( new Pacote( TipoPacote.CHECK_IN, usuario, getIdentificador(), getIdentificador() ) );

		} catch( PacoteInvalidoException pacoteInvalidoException ){
			pacoteInvalidoException.printStackTrace();
		}

		try {
			Pacote pacote;

			streamEntrada = new ObjectInputStream( this.getSocket().getInputStream() );

			do {
				pacote = (Pacote)streamEntrada.readObject();

				if( !pacote.getIdentificadorDoRemetente().equals( getIdentificador() ) ){
					if( pacote.getTipoPacote() == TipoPacote.MENSAGEM ){
						avisarChatListeners( (String)pacote.getPayload() );
						
					} else if( pacote.getTipoPacote() == TipoPacote.MOVIMENTO ){
						avisarMouseListeners( (MouseEvent)pacote.getPayload() );
						
					} else if( pacote.getTipoPacote() == TipoPacote.CHECK_IN ){
						avisarSobreNovoUsuario( (Usuario)pacote.getPayload() );

					}  else if( pacote.getTipoPacote() == TipoPacote.CHECK_OUT ){
						avisarSobreUsuarioDesconectando( (Usuario)pacote.getPayload() );
					}
				}

			} while( pacote.getTipoPacote() != TipoPacote.CHECK_OUT );

		} catch( IOException ioException ){
			System.err.println( "Erro ao conectar: erro de E/S" );

		}
		catch( ClassNotFoundException classNotFoundException ){}
		catch( ClassCastException classCastException ){
			System.err.println( "Pacote inválido" );
		}
		finally{
			try {
				streamEntrada.close();
				destroy();
			}
			catch( NullPointerException nullPointerException ){}
			catch( IOException ioException ){}
		}
	}
	@Override
	public void run() {
		ObjectOutputStream streamSaida = null;

		try {
			Pacote pacote;

			streamSaida = new ObjectOutputStream( this.getSocket().getOutputStream() );

			do {
				pacote = bufferDeSaida.poll();
				
				if( pacote != null ){
					streamSaida.writeObject( pacote );
				}

			} while( pacote == null || !pacote.getTipoPacote().equals( TipoPacote.CHECK_OUT ) );

		} catch( IOException ioException ){
			System.err.println( "Erro ao enviar pacotes" );

		} finally {
			try {
				streamSaida.close();
				destroy();
			}
			catch( NullPointerException nullPointerException ){}
			catch( IOException io ){}
		}
	}

	@Override
	public void addChatListener( ChatListener chatListener ){
		this.chatListeners.add( chatListener );		
	}

	@Override
	public void removeChatListener( ChatListener chatListener ){
		this.chatListeners.remove( chatListener );
	}

	@Override
	public void addMouseListener( MouseListener mouseListener ){
		this.mouseListeners.add( mouseListener );
	}

	@Override
	public void addMouseMotionListener( MouseMotionListener mouseMotionListener ){
		this.mouseMotionListeners.add( mouseMotionListener );
	}

	@Override
	public void removeMouseMotionListener( MouseMotionListener mouseMotionListener ){
		this.mouseMotionListeners.remove( mouseMotionListener );
	}

	@Override
	public void removeMouseListener( MouseListener mouseListener ){
		this.mouseListeners.remove( mouseListener );
	}

	@Override
	public void addUsuarioListener(UsuarioListener usuarioListener) {
		this.usuarioListeners.add( usuarioListener );
	}

	@Override
	public void removeUsuarioListener(UsuarioListener usuarioListener) {
		this.usuarioListeners.remove( usuarioListener );
	}

	private void avisarSobreNovoUsuario( Usuario usuario ){
		for( UsuarioListener usuarioListener : this.usuarioListeners ){
			usuarioListener.adicionarUsuario( usuario );
		}
	}

	private void avisarSobreUsuarioDesconectando( Usuario usuario ){
		for( UsuarioListener usuarioListener : this.usuarioListeners ){
			usuarioListener.removerUsuario( usuario );
		}
	}

	private void avisarMouseListeners( MouseEvent mouseEvent ){
		if( mouseEvent != null ){
			String evento = mouseEvent.paramString().split( "[,]" )[ 0 ];

			if( evento.equals( "MOUSE_DRAGGED" ) ){
				for( MouseMotionListener mouseMotionListener : this.mouseMotionListeners ){
					mouseMotionListener.mouseDragged( mouseEvent );
				}

			} else if( evento.equals( "MOUSE_PRESSED" ) ){
				for( MouseListener mouseListener : this.mouseListeners ){
					mouseListener.mousePressed( mouseEvent );
				}

			} else if( evento.equals( "MOUSE_RELEASED" ) ){
				for( MouseListener mouseListener : this.mouseListeners ){
					mouseListener.mouseReleased( mouseEvent );
				}
			}
		}
	}

	private void avisarChatListeners( String mensagem ){
		for( ChatListener chatListener : this.chatListeners ){
			chatListener.tratarNovaMensagem( mensagem );
		}
	}
}